package com.pixelengine;

import org.apache.hadoop.hbase.util.Bytes;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.Driver;
import org.gdal.gdal.gdal;

import java.io.File;

//导出数据到geotiff
//将WorderWorker中的导出代码移动到这个类里。
public class WExportProcesser {
    private byte[] filldataBufferByte = null ;//byte
    private int[] filldataBufferInt = null ;//int16 uint16 int32
    private float[] filldataBufferFloat = null ;//float
    private double[] filldataBufferDouble = null ;//uint32 double

    private void fillNodataValue( Dataset outds , int ixtileOffset, int iytileOffset, int tileSize , int nbands, int outdsDataType, double fillvalue)
    {
        int atilesize = tileSize*tileSize ;
        if( outdsDataType==1 ){
            if( filldataBufferByte == null ) {
                filldataBufferByte = new byte[tileSize*tileSize] ;
                for(int i = 0 ; i<atilesize;++i ) filldataBufferByte[i] = (byte)fillvalue ;
            }
            for(int ib = 0 ; ib<nbands;++ib ){
                outds.GetRasterBand(ib+1).WriteRaster(tileSize*ixtileOffset,tileSize*iytileOffset, tileSize,tileSize, filldataBufferByte) ;
            }
        }else if( outdsDataType==2 || outdsDataType==3 || outdsDataType==5 )
        {
            if( filldataBufferInt == null ){
                filldataBufferInt = new int[tileSize*tileSize] ;
                for(int i = 0 ; i<atilesize;++i ) filldataBufferInt[i] = (int)fillvalue ;
            }
            for(int ib = 0 ; ib<nbands;++ib ){
                outds.GetRasterBand(ib+1).WriteRaster(tileSize*ixtileOffset,tileSize*iytileOffset, tileSize,tileSize, 5, filldataBufferInt ) ;
            }
        }else if( outdsDataType==6 )
        {
            if( filldataBufferFloat==null ){
                filldataBufferFloat = new float[tileSize*tileSize] ;
                for(int i = 0 ; i<atilesize;++i ) filldataBufferFloat[i] = (float)fillvalue ;
            }
            for(int ib = 0 ; ib<nbands;++ib ){
                outds.GetRasterBand(ib+1).WriteRaster(tileSize*ixtileOffset,tileSize*iytileOffset, tileSize,tileSize, 6, filldataBufferFloat ) ;
            }
        }else if( outdsDataType==4 || outdsDataType==7 )
        {
            if( filldataBufferDouble==null ){
                filldataBufferDouble = new double[tileSize*tileSize] ;
                for(int i = 0 ; i<atilesize;++i ) filldataBufferDouble[i] = fillvalue ;
            }
            for(int ib = 0 ; ib<nbands;++ib ){
                outds.GetRasterBand(ib+1).WriteRaster(tileSize*ixtileOffset,tileSize*iytileOffset, tileSize,tileSize, 7, filldataBufferDouble ) ;
            }
        }
    }

    private void fillTileResultData( Dataset outds , int ixtileOffset, int iytileOffset, int tileSize , int nbands, int outdsDataType, TileComputeResult tileRes)
    {
        int pixelByteLen = 1 ;
        if( outdsDataType<2 ) pixelByteLen=1 ;
        else if( outdsDataType<4 ) pixelByteLen = 2 ;
        else if( outdsDataType<7 ) pixelByteLen = 4 ;
        else if( outdsDataType==7 ) pixelByteLen = 8 ;
        for(int ib = 0 ; ib<nbands;++ib ){
            int bandByteOffset = ib * tileSize * tileSize * pixelByteLen ;
            byte[] buffer = Bytes.copy(tileRes.binaryData , bandByteOffset , tileSize * tileSize*pixelByteLen ) ;
            outds.GetRasterBand(ib+1).WriteRaster(tileSize*ixtileOffset,tileSize*iytileOffset, tileSize,tileSize, outdsDataType , buffer) ;
        }
    }

    public boolean exportPart(JExportPart part,int tileSize,
                                     int nbands,
                                     double fillvalue,
                                     double[] geoTrans,
                                     int dataType,
                                     HBasePeHelperCppConnector cc,
                                     String scriptWithSDUI,
                                     String extraText,
                                     int targetZ,
                                     String pedir,
                              String relTifFilename,
                              String gdalwarpCommand,
                              String clipGeojsonFilename
    ) throws Exception {
        ///////////////////////////////////////////////////////////////////
        //
        //在存储中打开结果文件
        String outTiffFilepath = pedir+relTifFilename  ;
        String tempOutTifFilepath = outTiffFilepath.replace(".tif" , "_tiled.tif") ;//未裁剪数据
        System.out.println("create output tif file " + outTiffFilepath );
        int outxsize = (part.stopTileXExclude-part.startTileXInclude)*tileSize ;
        int outysize = (part.stopTileYExclude-part.startTileYInclude)*tileSize ;
        if( outxsize<=0 || outysize<=0 ){
            throw new Exception("bad x,y size: "+outxsize+","+outysize);
        }
        Driver driver = gdal.GetDriverByName("GTIFF") ;
        if( driver==null ){
            throw new Exception("gdal has no GTIFF driver available");
        }
        String[] options = new String[1] ;
        options[0] = "COMPRESS=Deflate" ;
        Dataset outputds = driver.Create(tempOutTifFilepath, outxsize,outysize,
                nbands, dataType, options ) ;
        if( outputds==null ){
            throw new Exception("gdal create output dataset failed");
        }
        outputds.SetGeoTransform(geoTrans) ;
        outputds.SetProjection("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]");

        //设置无效值
        for(int ib = 0 ; ib<nbands;++ib ){
            outputds.GetRasterBand(ib+1).SetNoDataValue( fillvalue) ;
        }


        ///////////////////////////////////////////////////////
        //
        //开始瓦片循环，调用v8计算，每个瓦片计算结果写入geotiff
        int numGoodTile = 0 ;
        int numBadTile = 0 ;
        int tileCount = (part.stopTileXExclude-part.startTileXInclude)*
                (part.stopTileYExclude-part.startTileYInclude);
        int tileCounter = 0 ;
        int per0 = -1 ;
        for(int iytile = part.startTileYInclude ; iytile < part.stopTileYExclude ; ++ iytile)
        {
            for(int ixtile = part.startTileXInclude; ixtile <part.stopTileXExclude; ++ ixtile )
            {
                ++ tileCounter ;
                TileComputeResult tileRes = cc.RunScriptForTileWithoutRenderWithExtra(
                        "com/pixelengine/HBasePixelEngineHelper",
                        scriptWithSDUI,
                        extraText,
                        targetZ,
                        iytile,ixtile
                ) ;
                if(tileRes==null || tileRes.status!=0 ){
                    //填充无效值
                    fillNodataValue(outputds ,
                            ixtile - part.startTileXInclude ,
                            iytile - part.startTileYInclude ,
                            tileSize ,
                            nbands ,
                            dataType ,
                            fillvalue
                    );
                    ++numBadTile ;
                }else{
                    //有效计算
                    fillTileResultData(outputds ,
                            ixtile - part.startTileXInclude ,
                            iytile - part.startTileYInclude ,
                            tileSize ,
                            nbands ,
                            dataType ,
                            tileRes );
                    ++numGoodTile ;
                }
                int per1 = tileCounter*100 / tileCount ;
                if( per1!=per0){
                    per0 = per1 ;
                    System.out.print( per0 + "% ");
                }
            }
        }
        System.out.println( " All tile compute done. good:"+numGoodTile+", bad:"+numBadTile);

        ///////////////////////////////////////////////////////
        //
        //关闭文件
        System.out.println("close outputds.");
        outputds.FlushCache();
        outputds.delete();

        ///////////////////////////////////////////////////////
        //
        //geojson裁剪
        System.out.println("start geojson cliping");
        String clipCmd = gdalwarpCommand
                + " -co COMPRESS=DEFLATE "  //fix 这里使用双引号"COMPRESS=DEFLATE"导致调用gdalwarp结果没有压缩，不知道为什么
                + " -srcnodata " + fillvalue
                + " -dstnodata " + fillvalue
                + " -cutline " + clipGeojsonFilename
                //+ " -crop_to_cutline " //fix 当ROI大于栅格的时候，栅格会使用填充值把这个空间填上，造成极大浪费
                + " " + tempOutTifFilepath
                + " "
                + outTiffFilepath ;
        System.out.println("clip command:" + clipCmd);
        Runtime.getRuntime().exec(clipCmd).waitFor() ;

        ///////////////////////////////////////////////////////
        //
        //检查裁剪结果是否存在
        System.out.println("check clip result tif exists");
        File clipResultFile = new File( outTiffFilepath ) ;
        if( clipResultFile.exists()==false ){
            throw new Exception("clip result tif is not available") ;
        }else{
            System.out.println("clip result file exists.") ;
        }
        System.out.println("try delete temp file");
        File tempFile = new File(tempOutTifFilepath) ;
        if( tempFile.delete() ) {
            System.out.println("successfully delete temp file " + tempOutTifFilepath);
        }else{
            System.out.println("failed to delete temp file "+tempOutTifFilepath);
        }
        return true ;
    }
}
